﻿using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.Linq;
using System.Text;

using FRL.DebtChased.Extensions;
using FRL.DebtChased.WebServices;

namespace FRL.DebtChased.Types
{

	/// <summary>
	/// Declaration of Money class
	/// </summary>
	public partial class Money
	{

		/////////////////////////////////////////////////////////////
		// Enumerations

		public enum eCurrency
		{
			NotSet = 0,
			Mixed,
			Sterling,
			USDollar,
			Euro,
			Yen
		}

		/////////////////////////////////////////////////////////////
		// Constants

		private const char GBPound = '£';
		private const char USDollar = '$';
		private const char Euro = '€';
		private const char Yen = '¥';

		public const string SterlingCode = "GBP";
		public const string USDollarCode = "USD";
		public const string EuroCode = "EUR";
		public const string YenCode = "JPY";

		/////////////////////////////////////////////////////////////
		// Attributes

		private decimal m_dcAmount;
		public decimal Amount
		{
			get { return m_dcAmount; }
			set { m_dcAmount = value; }
		}

		private eCurrency m_Currency;
		public eCurrency Currency
		{
			get { return m_Currency; }
		}

		/////////////////////////////////////////////////////////////
		// Construction

		public Money( Money Source )
		{

			// Initialize member variables
			m_dcAmount = Source.m_dcAmount;
			m_Currency = Source.m_Currency;

		}

		public Money( decimal dcAmount, eCurrency Currency )
		{

			// Initialize member variables
			m_dcAmount = dcAmount;
			m_Currency = Currency;

		}

		public Money( decimal dcAmount, string strCurrencyCode )
		{

			// Initialize member variables
			m_dcAmount = dcAmount;
			m_Currency = ParseCurrencyCode(strCurrencyCode);
#if DEBUG
			Debug.Assert(m_Currency != eCurrency.NotSet);
#endif // #if DEBUG

		}

		public Money(CurrencyTransport currencyTransport)
		{

			// Initialize member variables
			m_dcAmount = currencyTransport.Amount;
			m_Currency = ParseCurrencyCode(currencyTransport.CurrencyCode);
#if DEBUG
			Debug.Assert(m_Currency != eCurrency.NotSet);
#endif // #if DEBUG
		
		}

		/////////////////////////////////////////////////////////////////
		#region Implementation

		public override string ToString()
		{

			// Call sibling function
			return Format(true);

		}

		public override bool Equals(object obj)
		{
			Money mn = (Money)obj;
			if (object.Equals(mn, null))
				return false;
			return (
				(mn.Currency == this.Currency)
				&&
				(mn.Amount == this.Amount)
			);
		}

		public override int GetHashCode()
		{
			return this.ToString().GetHashCode();
		}

		public static bool operator ==(Money mn1, Money mn2)
		{
			if (object.Equals(mn1, null))
				return object.Equals(mn2, null);
			else
				return mn1.Equals(mn2);
		}

		public static bool operator !=(Money mn1, Money mn2)
		{
			if (object.Equals(mn1, null))
				return (!object.Equals(mn2, null));
			else
				return !mn1.Equals(mn2);
		}

		public static Money operator +(Money mn1, Money mn2)
		{

			// shortcuts
			if (
				(mn1.Currency == eCurrency.Mixed)
				||
				(mn2.Currency == eCurrency.Mixed)
			)
				return new Money(0, eCurrency.Mixed);
			if(
				(mn1.Currency == eCurrency.NotSet)
				||
				(mn2.Currency == eCurrency.NotSet)
			)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return new Money(0, eCurrency.Mixed);
			}

			// If currencies do not match...
			if(mn1.Currency != mn2.Currency)
				return new Money( 0, eCurrency.Mixed );
			else
				return new Money( mn1.Amount + mn2.Amount, mn1.Currency );

		}

		public static Money operator -(Money mn1, Money mn2)
		{

			// shortcuts
			if (
				(mn1.Currency == eCurrency.Mixed)
				||
				(mn2.Currency == eCurrency.Mixed)
			)
				return new Money(0, eCurrency.Mixed);
			if (
				(mn1.Currency == eCurrency.NotSet)
				||
				(mn2.Currency == eCurrency.NotSet)
			)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return new Money(0, eCurrency.Mixed);
			}

			// If currencies do not match...
			if (mn1.Currency != mn2.Currency)
				return new Money(0, eCurrency.Mixed);
			else
				return new Money(mn1.Amount - mn2.Amount, mn1.Currency);

		}

		#endregion Implementation

		/////////////////////////////////////////////////////////////
		// Operations

		public string Format( bool bWithBrackets )
		{
#if DEBUG
			Debug.Assert(this.Currency != eCurrency.NotSet);
#endif // #if DEBUG

			// shortcuts
			if (this.Currency == eCurrency.Mixed)
				return "[MIXED]";
			if (this.Currency == eCurrency.NotSet)
				return "[UNKNOWN]";

			// Format a string
			StringBuilder sb = new StringBuilder();

			// If formating negatives with brackets...
			if (bWithBrackets)
			{

				// Use brackets for negative values, example, $(123.45)

				// Prefix with currency sign
				sb.Append(CurrencyToSymbol(m_Currency));

				// Format decimal
				if (this.m_dcAmount < 0)
					sb.AppendFormat("({0:#,0.00})", Math.Abs(this.m_dcAmount));
				else
					sb.AppendFormat("{0:#,0.00}", this.m_dcAmount);

			}
			else
			{

				// Use minus sign, example, -$123.45

				// Add sign
				if (this.m_dcAmount < 0)
					sb.Append("-");

				// Add currency
				sb.Append(CurrencyToSymbol(m_Currency));

				// Add value
				sb.Append(Math.Abs(this.Amount).ToString("#,0.00"));

			}

			// Return string
			return sb.ToString();

		}

		/////////////////////////////////////////////////////////////
		// Static Operations

		public static Money.eCurrency ParseCurrencyCode( string strCode )
		{

			// declare return variable
			eCurrency Ftmp = eCurrency.NotSet;

			// Compare currencies
#if SILVERLIGHT
			if ((string.Compare(strCode, GBPound.ToString(), StringComparison.CurrentCulture) == 0) || (string.Compare(strCode, SterlingCode, StringComparison.CurrentCulture) == 0))
				Ftmp = eCurrency.Sterling;
			else if ((string.Compare(strCode, USDollar.ToString(), StringComparison.CurrentCulture) == 0) || (string.Compare(strCode, USDollarCode, StringComparison.CurrentCulture) == 0))
				Ftmp = eCurrency.USDollar;
			else if ((string.Compare(strCode, Euro.ToString(), StringComparison.CurrentCulture) == 0) || (string.Compare(strCode, EuroCode, StringComparison.CurrentCulture) == 0))
				Ftmp = eCurrency.Euro;
			else if ((string.Compare(strCode, Yen.ToString(), StringComparison.CurrentCulture) == 0) || (string.Compare(strCode, YenCode, StringComparison.CurrentCulture) == 0))
				Ftmp = eCurrency.Yen;
#else
			if ((string.Compare(strCode, GBPound.ToString(), true) == 0) || (string.Compare(strCode, SterlingCode, true) == 0))
				Ftmp = eCurrency.Sterling;
			else if ((string.Compare(strCode, USDollar.ToString(), true) == 0) || (string.Compare(strCode, USDollarCode, true) == 0))
				Ftmp = eCurrency.USDollar;
			else if ((string.Compare(strCode, Euro.ToString(), true) == 0) || (string.Compare(strCode, EuroCode, true) == 0))
				Ftmp = eCurrency.Euro;
			else if ((string.Compare(strCode, Yen.ToString(), true) == 0) || (string.Compare(strCode, YenCode, true) == 0))
				Ftmp = eCurrency.Yen;
#endif // #if SILVERLIGHT

			// return variable
			return Ftmp;

		}

		public static string CurrencyToCode(eCurrency Currency)
		{

			// declare return variable
			string strFtmp = string.Empty;

			// Compare currencies
			switch (Currency)
			{
				case eCurrency.Sterling: strFtmp = "GBP"; break;
				case eCurrency.USDollar: strFtmp = "USD"; break;
				case eCurrency.Euro: strFtmp = "EUR"; break;
				case eCurrency.Yen: strFtmp = "JPY"; break;
#if DEBUG
				default:
					Debug.Assert(false);
					break;
#endif // #if DEBUG
			} // switch

			// return variable
			return strFtmp;

		}

		public static string CurrencyToSymbol(eCurrency Currency)
		{

			// Compare codes
			if (Currency == eCurrency.Sterling)
				return GBPound.ToString();
			else if (Currency == eCurrency.USDollar)
				return USDollar.ToString();
			else if (Currency == eCurrency.Euro)
				return Euro.ToString();
			else if (Currency == eCurrency.Yen)
				return Yen.ToString();
			else
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return string.Empty;
			}

		}

		public static string SuperSmartAddition( List<Money> moneyList, bool bWithBrackets )
		{
#if DEBUG
			Debug.Assert(moneyList.Count > 0);
#endif // #if DEBUG

			// Declare return variable
			string strFtmp;

			// Declaration of local variables
			decimal dcTotal = 0M;

			// Check for homogenous currency
			bool bHomogenousCurrency = true;
			dcTotal = 0.00M;
			for (int i1 = 0; i1 < moneyList.Count; i1++)
			{
				if (moneyList[i1].Currency == moneyList[0].Currency)
					dcTotal += moneyList[i1].Amount;
				else
				{
					bHomogenousCurrency = false;
					break;
				}
			}

			// If homogenous currency...
			if (bHomogenousCurrency)
			{

				// Format currency
				strFtmp = new Money(dcTotal, moneyList[0].Currency).Format(bWithBrackets);

			}
			else
			{

				// Get list of currencies
				eCurrency[] currencyList = new eCurrency[4] {
					eCurrency.Sterling,
					eCurrency.USDollar,
					eCurrency.Euro,
					eCurrency.Yen
				};

				// Add up by grouping currencies
				List<string> strTotals = new List<string>();
				foreach (eCurrency c in currencyList)
				{

					// Calculate total for this currency
					dcTotal = 0.00M;
					foreach (Money mn in moneyList)
					{
						if (mn.Currency == c)
							dcTotal += mn.Amount;
					}

					// If there is something to add...
					if (dcTotal != 0)
						strTotals.Add( new Money(dcTotal,c).Format(bWithBrackets) );

				} // forloop

				// Build a string from the list
				strFtmp = strTotals.ToAndList();

			}

			// Return variable
			return strFtmp;

		}

	}

}
